// 版权所有2016 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package pprof

import (
	"context"
	"fmt"
	"sort"
	"strings"
)

type label struct {
	key   string
	value string
}

// 标签集是一组标签。
type LabelSet struct {
	list []label
}

// labelContextKey是用于探查器标签的ContextKey类型。
type labelContextKey struct{}

func labelValue(ctx context.Context) labelMap {
	labels, _ := ctx.Value(labelContextKey{}).(*labelMap)
	if labels == nil {
		return labelMap(nil)
	}
	return *labels
}

// labelMap是上下文类型中保存的标签集的表示形式。
// 这是一个初始实现，但它将被更有效地允许增量不可变修改的
// 替代。
type labelMap map[string]string

// 字符串统计Stringer并以一致的顺序返回键和值对
// 顺序。
func (l *labelMap) String() string {
	if l == nil {
		return ""
	}
	keyVals := make([]string, 0, len(*l))

	for k, v := range *l {
		keyVals = append(keyVals, fmt.Sprintf("%q:%q", k, v))
	}

	sort.Strings(keyVals)

	return "{" + strings.Join(keyVals, ", ") + "}"
}

// WithLabels返回一个新的上下文。添加了给定标签的上下文。
// 标签会用相同的键覆盖先前的标签。
func WithLabels(ctx context.Context, labels LabelSet) context.Context {
	childLabels := make(labelMap)
	parentLabels := labelValue(ctx)
	// TODO（matloob）：将映射实现替换为更高效的实现，因此使用标签创建子上下文不需要克隆映射。
	for k, v := range parentLabels {
		childLabels[k] = v
	}
	for _, label := range labels.list {
		childLabels[label.key] = label.value
	}
	return context.WithValue(ctx, labelContextKey{}, &childLabels)
}

// Labels获取表示键值对的偶数字符串
// 并生成包含它们的标签集。
// 标签会用相同的键覆盖先前的标签。
// 目前只有CPU和goroutine配置文件使用任何标签
// 信息。
// 见https:
func Labels(args ...string) LabelSet {
	if len(args)%2 != 0 {
		panic("uneven number of arguments to pprof.Labels")
	}
	list := make([]label, 0, len(args)/2)
	for i := 0; i+1 < len(args); i += 2 {
		list = append(list, label{key: args[i], value: args[i+1]})
	}
	return LabelSet{list: list}
}

// Label返回ctx上具有给定键的标签值，以及指示
// 该标签是否存在的布尔值。
func Label(ctx context.Context, key string) (string, bool) {
	ctxLabels := labelValue(ctx)
	v, ok := ctxLabels[key]
	return v, ok
}

// ForLabels使用上下文中设置的每个标签调用f。
// 函数f应返回true继续迭代，或返回false提前停止迭代。
func ForLabels(ctx context.Context, f func(key, value string) bool) {
	ctxLabels := labelValue(ctx)
	for k, v := range ctxLabels {
		if !f(k, v) {
			break
		}
	}
}
